package com.yihaodian.search.core;

import java.util.concurrent.atomic.AtomicLong;

import org.apache.log4j.Logger;

import com.google.inject.Inject;
import com.yihaodian.api.search.SearchException;
import com.yihaodian.api.search.SearchResponse;
import com.yihaodian.api.search.SugRequest;
import com.yihaodian.api.search.SugResponse;
import com.yihaodian.cluster.Node;
import com.yihaodian.index.IndexShard;
import com.yihaodian.index.meta.shard.Shard;
import com.yihaodian.indices.IndicesService;
import com.yihaodian.search.SearchContext;
import com.yihaodian.search.broker.shard.ShardSearchRequest;
import com.yihaodian.search.broker.shard.ShardSearchResponse;
import com.yihaodian.search.broker.shard.ShardSearchService;
import com.yihaodian.search.facet.FacetHandler;
import com.yihaodian.search.fetch.FetchPhase;
import com.yihaodian.search.fetch.FetchRequest;
import com.yihaodian.search.fetch.FetchSearchResult;
import com.yihaodian.search.grouping.GroupingHandler;
import com.yihaodian.search.highlight.HighlightHandler;
import com.yihaodian.search.query.QueryPhase;
import com.yihaodian.search.query.QuerySearchResult;
import com.yihaodian.server.LifeCycleService;

public class LocalSearchService implements ShardSearchService, LifeCycleService {
	static Logger logger = Logger.getLogger(LocalSearchService.class);

	private final AtomicLong idGenerator = new AtomicLong();

	// TODO Inject the instance.

	private QueryPhase queryPhrase = new QueryPhase();

	private FetchPhase fetchPhrase = new FetchPhase();

	private SearchHandler searchHandler = new SearchHandler();

	IndicesService indicesService;

	Node node;

	@Inject
	public LocalSearchService(IndicesService indicesService, Node node) {
		this.indicesService = indicesService;
		this.node = node;
	}

	@Override
	public void startup() {

	}

	@Override
	public void shutdown() {
		// TODO Auto-generated method stub

	}

	public ShardSearchResponse executeSearch(ShardSearchRequest request)
			throws SearchException {
		Shard shard = new Shard(request.getSearchRequest().getIndexName(),
				request.getShardId());

		try {
			IndexShard indexShard = indicesService.getIndexShard(shard
					.getShardId());
			if (indexShard == null) {
				throw new SearchException("IndexShard is not create yet"
						+ shard);
			}
			Searcher searcher = indexShard.getSearcher();
			SearchResponse searchResponse = new SearchResponse();

			SearchContext context = new SearchContext(request, searchResponse,
					searcher);

			// search
			searchHandler.handle(context);
			// faceting
			FacetHandler.faceting(context);
			// grouping
			GroupingHandler.grouping(context);
			// highlight
			HighlightHandler.highlight(context);

			searchResponse.setNode(node.getId());

			ShardSearchResponse response = new ShardSearchResponse(
					request.getShardId(), searchResponse);
			return response;
		} catch (Exception e) {
			throw new SearchException(e.getMessage());
		}
	}

	// @Override
	public QuerySearchResult executeQueryPhrase(ShardSearchRequest request)
			throws SearchException {
		/**
		 * TODO: 1.Reuse the context in fetch phrase. 2.Clean context for
		 * localthread when query end. 3.Discuss: Add/Change interface affect
		 * mandy other class. Need imporvement?
		 */

		SearchContext context = createContext(request);
		queryPhrase.execute(context);

		return context.getQueryResult();
	}

	// @Override
	public FetchSearchResult executeFetchPhrase(FetchRequest fetchReq)
			throws SearchException {
		/**
		 * TODO: Reuse the context in query phase.
		 */
		SearchContext context = createContext(fetchReq.request);
		queryPhrase.execute(context);
		fetchPhrase.execute(context);

		return context.getFetchResult();
	}

	private SearchContext createContext(ShardSearchRequest request) {
		Shard shard = new Shard(request.getSearchRequest().getIndexName(),
				request.getShardId());

		IndexShard indexShard = indicesService
				.getIndexShard(shard.getShardId());
		SearchContext context;
		try {
			context = new SearchContext(idGenerator.incrementAndGet(), shard,
					request, indexShard.getSearcher());
		} catch (Exception e) {
			throw new SearchException(e);
		}

		return context;
	}

	@Override
	public SugResponse executeSug(String indexName, String shardId,
			SugRequest request) throws SearchException {
		Shard shard = new Shard(indexName, shardId);

		IndexShard indexShard = indicesService
				.getIndexShard(shard.getShardId());

		try {
			Suggester predictor = indexShard.getSuggester(request.getField());
			SugResponse response = predictor.suggest(request);

			return response;
		} catch (Exception e) {
			throw new SearchException(e);
		}
	}
}
